package work.linruchang.util;

import cn.hutool.core.bean.BeanPath;
import cn.hutool.core.comparator.CompareUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.lang.Dict;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.javaprop.JavaPropsMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper;
import com.fasterxml.jackson.dataformat.yaml.util.StringQuotingChecker;
import com.google.javascript.jscomp.CommandLineRunner;
import com.googlecode.htmlcompressor.compressor.HtmlCompressor;
import com.yahoo.platform.yui.compressor.CssCompressor;
import lombok.SneakyThrows;

import java.io.*;
import java.lang.reflect.Constructor;
import java.nio.charset.StandardCharsets;
import java.util.Properties;

/**
 * 内容转换工具类
 * @author LinRuChang
 * @version 1.0
 * @date 2022/11/03
 * @since 1.8
 **/
public class ContentConverUtil {

    private static ObjectMapper getYamlMappper() {
        YAMLFactory yamlFactory = YAMLFactory.builder()
                .enable(YAMLGenerator.Feature.MINIMIZE_QUOTES)
                .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER)
                .stringQuotingChecker(new StringQuotingChecker.Default() {
                    private static final long serialVersionUID = 1324053635146744895L;
                    @Override
                    public boolean needToQuoteValue(String value) {
                        return false;
                    }
                }).build();
        return new YAMLMapper(yamlFactory);
    }

    /**
     * properties转yaml
     * @param propertiesContent
     * @return
     */
    @SneakyThrows
    public static String propertiesToYaml(String propertiesContent) {
        if(StrUtil.isBlank(propertiesContent)) {return propertiesContent;}

        //仅有单层的map
        Properties properties = new Properties();
        properties.load(IoUtil.toStream(propertiesContent.getBytes(StandardCharsets.UTF_8)));

        //具体层级的Map
        Dict propertiesDict = Dict.create();
        properties.entrySet().stream()
                .sorted((mapEntry1,mapEntry2) -> -CompareUtil.compare(Convert.toStr(mapEntry1.getKey()).length(), Convert.toStr(mapEntry2.getKey()).length()))
                .forEach(mapEntry -> {
                    Object KeyName = mapEntry.getKey();
                    Object keyValue = mapEntry.getValue();
                    BeanPath.create(KeyName.toString()).set(propertiesDict,keyValue);
                });

        ObjectMapper yamlMapper = getYamlMappper();
        return yamlMapper.writeValueAsString(propertiesDict);
    }

    /**
     * 解析Yaml内容
     * @param yamlContent yaml内容
     * @return
     */
    @SneakyThrows
    public static JSONObject parseYaml(String yamlContent) {
        if(StrUtil.isBlank(yamlContent)) {
            return new JSONObject();
        }


        YAMLMapper yamlMapper = new YAMLMapper();

        JsonNode jsonNode = yamlMapper.readTree(yamlContent);
        JSONObject result = JSONUtil.parseObj(jsonNode.toString());

        return result;
    }

    /**
     * yaml转properties
     * @param yamlContent
     * @return
     */
    @SneakyThrows
    public static String yamlToProperties(String yamlContent) {
        JSONObject entries = parseYaml(yamlContent);
        JavaPropsMapper mapper = new JavaPropsMapper();
        return mapper.writeValueAsString(entries);
    }


    /**
     * json转properties
     * @param json
     * @return
     */
    @SneakyThrows
    public static String jsonToProperties(String json) {
        if(JSONUtil.isTypeJSON(json)) {
            JavaPropsMapper javaPropsMapper = new JavaPropsMapper();
            JSONObject jsonObject = JSONUtil.parseObj(json);
            return javaPropsMapper.writeValueAsString(jsonObject);
        }
        return null;
    }

    /**
     * properties转json
     * @param propertiesContent
     * @return
     */
    @SneakyThrows
    public static String propertiesToJson(String propertiesContent) {
        if(StrUtil.isBlank(propertiesContent)) {return propertiesContent;}

        //仅有单层的map
        Properties properties = new Properties();
        properties.load(IoUtil.toStream(propertiesContent.getBytes(StandardCharsets.UTF_8)));

        //具体层级的Map
        Dict propertiesDict = Dict.create();
        properties.entrySet().stream()
                .sorted((mapEntry1,mapEntry2) -> -CompareUtil.compare(Convert.toStr(mapEntry1.getKey()).length(), Convert.toStr(mapEntry2.getKey()).length()))
                .forEach(mapEntry -> {
                    Object KeyName = mapEntry.getKey();
                    Object keyValue = mapEntry.getValue();
                    BeanPath.create(KeyName.toString()).set(propertiesDict,keyValue);
                });

        return JSONUtil.toJsonPrettyStr(propertiesDict);
    }


    /**
     * json转yaml
     * @param json
     * @return
     */
    @SneakyThrows
    public static String jsonToYaml(String json) {
        if(JSONUtil.isTypeJSON(json)) {
            ObjectMapper yamlMappper = getYamlMappper();
            JSONObject jsonObject = JSONUtil.parseObj(json);
            return yamlMappper.writeValueAsString(jsonObject);
        }
        return null;
    }

    /**
     * yaml转json
     * @param yaml
     * @return
     */
    @SneakyThrows
    public static String yamlToJson(String yaml) {
        if(StrUtil.isBlank(yaml)) { return yaml;}
        YAMLMapper yamlMapper = new YAMLMapper();
        JsonNode jsonNode = yamlMapper.readTree(yaml);
        return JSONUtil.formatJsonStr(jsonNode.toString());
    }

    /**
     * CSS压缩
     * @param cssCotnent 待压缩的css内容
     * @return
     */
    @SneakyThrows
    public static String cssCompressor(String cssCotnent) {
        if(StrUtil.isNotBlank(cssCotnent)) {
            BufferedReader reader = null;
            ByteArrayOutputStream resultOutputStream = null;
            Writer writer = null;
            try {
                reader = IoUtil.getReader(new ByteArrayInputStream(cssCotnent.getBytes()), StandardCharsets.UTF_8);
                CssCompressor cssCompressor = new CssCompressor(reader);
                resultOutputStream = new ByteArrayOutputStream();
                // Writer writer = IoUtil.getWriter(resultOutputStream, StandardCharsets.UTF_8);
                writer = new OutputStreamWriter(System.out, StandardCharsets.UTF_8);
                cssCompressor.compress(writer, -1);
            }finally {
                IoUtil.close(reader);
                IoUtil.close(writer);  //必须先关闭流，字符数组才有数据
            }
            return IoUtil.toStr(resultOutputStream, StandardCharsets.UTF_8);
        }
        return cssCotnent;
    }

    private static File tempJavascriptDir = FileUtil.mkdir("temp/generateCode/javascript");

    /**
     * Javascript压缩
     *
     * @param javascriptCotnent 待压缩的javascript内容
     * @return
     */
    @SneakyThrows
    public static String javascriptCompressor(String javascriptCotnent) {
        String result = javascriptCotnent;
        if (StrUtil.isNotBlank(javascriptCotnent)) {
            File rowjavascriptCotnentTempFile = FileUtil.createTempFile(tempJavascriptDir);
            File compressorjavascriptCotnentTempFile = FileUtil.file(tempJavascriptDir, StrUtil.format("{}.min.js", IdUtil.getSnowflakeNextIdStr()));
            try {
                FileUtil.writeUtf8String(javascriptCotnent,rowjavascriptCotnentTempFile);
                String[] args = new String[]{"--js", FileUtil.getAbsolutePath(rowjavascriptCotnentTempFile), "--js_output_file", FileUtil.getAbsolutePath(compressorjavascriptCotnentTempFile), "--warning_level", "QUIET"};
                Constructor<CommandLineRunner> constructor = ReflectUtil.getConstructor(CommandLineRunner.class, String[].class);
                CommandLineRunner commandLineRunner = constructor.newInstance((Object)args);
                ReflectUtil.invoke(commandLineRunner, "doRun", new Object[0]);
                result = FileUtil.readString(compressorjavascriptCotnentTempFile, StandardCharsets.UTF_8);
            } finally {
                FileUtil.del(rowjavascriptCotnentTempFile);
                FileUtil.del(compressorjavascriptCotnentTempFile);
            }
        }
        return result;
    }

    @SneakyThrows
    public static String htmlCompressor(String htmlCotnent) {
        if(StrUtil.isNotBlank(htmlCotnent)) {
            HtmlCompressor htmlCompressor = new HtmlCompressor();
            return htmlCompressor.compress(htmlCotnent);
        }
        return htmlCotnent;
    }

}
